package sany.iec.service.impl;

import com.serotonin.modbus4j.BasicProcessImage;
import com.serotonin.modbus4j.ModbusSlaveSet;
import com.serotonin.modbus4j.ProcessImage;
import com.serotonin.modbus4j.ProcessImageListener;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.ip.tcp.TcpSlave;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import sany.iec.entity.DataEntity;
import sany.iec.entity.ServerEntity;
import sany.iec.enums.ModbusDataTypeEnum;
import sany.iec.enums.ModbusRegisterEnum;
import sany.iec.service.ModbusServerService;
import sany.iec.utils.ModbusTransformerUtil;

import javax.annotation.Resource;

/**
 * @Author 托马斯
 * @Date 2025/7/6 18:24
 * @Description ModbusService
 */
@Service
@Slf4j
public class ModbusServiceImpl implements ModbusServerService {

    @Override
    public void startServer(ServerEntity data, ModbusSlaveSet server) {
        new Thread(() -> {
            BasicProcessImage modscanProcessImage = getModscanProcessImage(data);
            server.addProcessImage(modscanProcessImage);
            try {
                server.start();
            } catch (ModbusInitException e) {
                log.error("ModbusServer start error", e);
                throw new RuntimeException(e);
            }
        }).start();
    }

    private BasicProcessImage getModscanProcessImage(ServerEntity data) {
        /* 初始化过程影像区 */
        BasicProcessImage processImage = new BasicProcessImage(data.getModbusId());
        /* 初始化过程影像区各个数值为0 */
        for (int i = 0; i < data.getDataLength(); i++) {
            processImage.setCoil(i, Math.random() > 0.5);
            processImage.setInput(i, Math.random() > 0.5);
            processImage.setHoldingRegister(i, (short) (Math.random() * 100));
            processImage.setInputRegister(i, (short) (Math.random() * 100));
        }
        /* 为影像区添加监听 */
        processImage.addListener(new BasicProcessImageListener(processImage));
        return processImage;
    }

    private static class BasicProcessImageListener implements ProcessImageListener {

        private BasicProcessImage processImage = null;

        public BasicProcessImageListener(BasicProcessImage processImage) {
            this.processImage = processImage;
        }

        @Override
        public void coilWrite(int i, boolean b, boolean b1) {
            System.out.println("Coil at " + i + "," + "change " + b + " to " + b1);
            if (processImage == null) {
                log.info("无法写入线圈。");
            }
            processImage.setCoil(i, b1);
            log.info("直接写入线圈成功");
        }

        @Override
        public void holdingRegisterWrite(int offset, short oldValue, short newValue) {
            System.out.println("Register at " + offset + "," + "change " + oldValue + " to " + newValue);
            if (processImage == null) {
                log.info("无法写入寄存器。");
            }
            processImage.setHoldingRegister(offset, newValue);
            log.info("直接写入保持寄存器成功");
        }
    }

    @Override
    public Float readCoil(ServerEntity server, DataEntity data) {
        if (!data.getModbusRegisterEnum().equals(ModbusRegisterEnum.COIL)) {
            return 0.0f;
        }
        try {
            ProcessImage processImage = server.getModbusServerSet().getProcessImage(server.getModbusId());
            return processImage.getCoil(data.getAddress()) ? 1.0f : 0.0f;
        } catch (Exception e) {
            log.error("readCoil error", e);
        }
        return 1.0f;
    }

    @Override
    public Float readInput(ServerEntity server, DataEntity data) {
        if (!data.getModbusRegisterEnum().equals(ModbusRegisterEnum.INPUT)) {
            return 0.0f;
        }
        try {
            ProcessImage processImage = server.getModbusServerSet().getProcessImage(server.getModbusId());
            return processImage.getInput(data.getAddress()) ? 1.0f : 0.0f;
        } catch (Exception e) {
            log.error("readInput error", e);
        }
        return 1.0f;
    }

    @Override
    public Float readHoldRegister(ServerEntity server, DataEntity data) {
        if (!data.getModbusRegisterEnum().equals(ModbusRegisterEnum.HOLD_REGISTER)) {
            return 0.0f;
        }
        try {
            ProcessImage processImage = server.getModbusServerSet().getProcessImage(server.getModbusId());
            if (data.getModbusDataTypeEnum().equals(ModbusDataTypeEnum.INT_AB) ||
                    data.getModbusDataTypeEnum().equals(ModbusDataTypeEnum.INT_BA)) {
                return ModbusTransformerUtil.getNumberAB(data, processImage.getHoldingRegister(data.getAddress())) * 1.0f;
            } else {
                return ModbusTransformerUtil.getNumberABCD(data,
                        processImage.getHoldingRegister(data.getAddress()),
                        processImage.getHoldingRegister(data.getAddress() + 1));
            }
        } catch (Exception e) {
            log.error("readHoldRegister error", e);
        }
        return 1.0f;
    }

    @Override
    public Float readInputRegister(ServerEntity server, DataEntity data) {
        if (!data.getModbusRegisterEnum().equals(ModbusRegisterEnum.INPUT_REGISTER)) {
            return 0.0f;
        }
        try {
            ProcessImage processImage = server.getModbusServerSet().getProcessImage(server.getModbusId());
            if (data.getModbusDataTypeEnum().equals(ModbusDataTypeEnum.INT_AB) ||
                    data.getModbusDataTypeEnum().equals(ModbusDataTypeEnum.INT_BA)) {
                return ModbusTransformerUtil.getNumberAB(data, processImage.getHoldingRegister(data.getAddress())) * 1.0f;
            } else {
                return ModbusTransformerUtil.getNumberABCD(data,
                        processImage.getInputRegister(data.getAddress()),
                        processImage.getInputRegister(data.getAddress() + 1));
            }
        } catch (Exception e) {
            log.error("readInputRegister error", e);
        }
        return 1.0f;
    }

    @Override
    public void writeCoil(ServerEntity server, DataEntity data, boolean value) {
        if (!data.getModbusRegisterEnum().equals(ModbusRegisterEnum.COIL)) {
            return;
        }
        ProcessImage processImage = server.getModbusServerSet().getProcessImage(server.getModbusId());
        processImage.setCoil(data.getAddress(), value);
        // log.info("写入线圈成功！！！！");
    }

    @Override
    public void writeInput(ServerEntity server, DataEntity data, boolean value) {
        if (!data.getModbusRegisterEnum().equals(ModbusRegisterEnum.INPUT)) {
            return;
        }
        ProcessImage processImage = server.getModbusServerSet().getProcessImage(server.getModbusId());
        processImage.setInput(data.getAddress(), value);
        // log.info("{} {}  输入变量", data.getAddress(), value);
    }

    @Override
    public void writeHoldRegister(ServerEntity server, DataEntity data, float value) {
        if (!data.getModbusRegisterEnum().equals(ModbusRegisterEnum.HOLD_REGISTER)) {
            return;
        }
        ProcessImage processImage = server.getModbusServerSet().getProcessImage(server.getModbusId());
        if (data.getModbusDataTypeEnum().equals(ModbusDataTypeEnum.INT_AB) || data.getModbusDataTypeEnum().equals(ModbusDataTypeEnum.INT_BA)) {
            Short num = ModbusTransformerUtil.getNumberToShort(data, value);
            processImage.setHoldingRegister(data.getAddress(), num);
        } else {
            Short[] num = ModbusTransformerUtil.getNumberToShortArray(data, value);
            processImage.setHoldingRegister(data.getAddress(), num[0]);
            processImage.setHoldingRegister(data.getAddress() + 1, num[1]);
        }
        // log.info("写入保持寄存器成功！！！！");
    }

    @Override
    public void writeInputRegister(ServerEntity server, DataEntity data, float value) {
        if (!data.getModbusRegisterEnum().equals(ModbusRegisterEnum.INPUT_REGISTER)) {
            return;
        }
        ProcessImage processImage = server.getModbusServerSet().getProcessImage(server.getModbusId());
        if (data.getModbusDataTypeEnum().equals(ModbusDataTypeEnum.INT_AB) || data.getModbusDataTypeEnum().equals(ModbusDataTypeEnum.INT_BA)) {
            Short num = ModbusTransformerUtil.getNumberToShort(data, value);
            processImage.setHoldingRegister(data.getAddress(), num);
        } else {
            Short[] num = ModbusTransformerUtil.getNumberToShortArray(data, value);
            processImage.setInputRegister(data.getAddress(), num[0]);
            processImage.setInputRegister(data.getAddress() + 1, num[1]);
        }
        // log.info("输入寄存器 {} {}  {} ", data.getAddress(), num[0], num[1]);
    }
}
